package com.macro.mall.utils;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.util.ClassUtils;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
import com.alibaba.excel.write.handler.WriteHandler;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.google.common.collect.Maps;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.poi.ss.usermodel.*;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.*;
import java.util.Map.Entry;

public class ExcelUtils {

    private static Map<String, Field> ignoreMap = Maps.newHashMap();

    public static <T> void writeImportTemplate(HttpServletResponse response, String fileName, String sheetName, List<T> data, Class<T> cls) throws IOException {
        write(response, fileName, sheetName, data, cls, null, null, true);
    }
    
    public static <T> void writeImportTemplate(HttpServletResponse response, String fileName, String sheetName, List<T> data, Class<T> cls, WriteHandler writeHandler) throws IOException {
        write(response, fileName, sheetName, data, cls, null, writeHandler, true);
    }

    public static <T> void write(HttpServletResponse response, String fileName, String sheetName, List<T> data, Class<T> cls) throws IOException {
        write(response, fileName, sheetName, data, cls, null, null, false);
    }

    public static <T> void write(HttpServletResponse response, String fileName, String sheetName, List<T> data, Class<T> cls,
            Collection<String> excludeColumnFiledNames) throws IOException {
        write(response, fileName, sheetName, data, cls, excludeColumnFiledNames, null, false);
    }

    public static <T> void write(HttpServletResponse response, String fileName, String sheetName, List<T> data, Class<T> cls, WriteHandler writeHandler) throws IOException {
        write(response, fileName, sheetName, data, cls, null, writeHandler, false);
    }

    public static <T> void write(HttpServletResponse response, String fileName, String sheetName, List<T> data, Class<T> cls, Collection<String> excludeColumnFiledNames,
            WriteHandler writeHandler, boolean isWriteImportTemplate) throws IOException {
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
        response.setHeader("Content-Disposition", "attachment;filename=" + fileName);

        ExcelWriterBuilder excelWriterBuilder = EasyExcel.write(response.getOutputStream(), cls);

        if (CollectionUtils.isNotEmpty(excludeColumnFiledNames)) {
            excelWriterBuilder.excludeColumnFiledNames(excludeColumnFiledNames);
        }
        if (writeHandler != null) {
            excelWriterBuilder.registerWriteHandler(writeHandler);
        }
        // 设置样式
        excelWriterBuilder.registerWriteHandler(getStyleStrategy(isWriteImportTemplate));
        // 自适应列宽
        excelWriterBuilder.registerWriteHandler(new CustomCellWriteHandler());

        excelWriterBuilder.sheet(sheetName).doWrite(data);
    }

    public static <T> TreeMap<Integer, String> templateHeadMap(Class<T> cls) {
        return templateHeadMap(cls, null);
    }

    public static <T> TreeMap<Integer, String> templateHeadMap(Class<T> cls, Collection<String> excludeColumnFiledNames) {
        List<Field> defaultFieldList = new ArrayList<Field>();
        Map<Integer, Field> customFiledMap = new TreeMap<Integer, Field>();
        ClassUtils.declaredFields(cls, defaultFieldList, customFiledMap, ignoreMap, null);
        if (CollectionUtils.isEmpty(defaultFieldList)) {
            ClassUtils.declaredFields(cls, defaultFieldList, null);
        }
        int index = 0;
        TreeMap<Integer, String> headMap = Maps.newTreeMap();
        Iterator<Entry<Integer, Field>> iterator = customFiledMap.entrySet().iterator();
        Field customFiled = null;
        for (Field field : defaultFieldList) {
            while (iterator.hasNext()) {
                customFiled = iterator.next().getValue();
                if (CollectionUtils.isNotEmpty(excludeColumnFiledNames) && excludeColumnFiledNames.contains(customFiled.getName())) {
                    continue;
                }
                headMap.put(index, getExcelPropertyValue(customFiled));
                index++;
            }

            if (CollectionUtils.isNotEmpty(excludeColumnFiledNames) && excludeColumnFiledNames.contains(field.getName())) {
                continue;
            }
            headMap.put(index, getExcelPropertyValue(field));
            index++;
        }

        return headMap;
    }

    private static String getExcelPropertyValue(Field field) {
        ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
        return excelProperty.value()[0];
    }

    public static <T> TreeMap<Integer, String> templateMultiHeadMap(Class<T> cls, int valueIndex) {
        return templateMultiHeadMap(cls, valueIndex, null);
    }

    public static <T> TreeMap<Integer, String> templateMultiHeadMap(Class<T> cls, int valueIndex, Collection<String> excludeColumnFiledNames) {
        List<Field> defaultFieldList = new ArrayList<Field>();
        Map<Integer, Field> customFiledMap = new TreeMap<Integer, Field>();
        ClassUtils.declaredFields(cls, defaultFieldList, customFiledMap, ignoreMap, null);
        if (CollectionUtils.isEmpty(defaultFieldList)) {
            ClassUtils.declaredFields(cls, defaultFieldList, null);
        }
        int index = 0;
        TreeMap<Integer, String> headMap = Maps.newTreeMap();
        for (Field field : defaultFieldList) {
            if (CollectionUtils.isNotEmpty(excludeColumnFiledNames) && excludeColumnFiledNames.contains(field.getName())) {
                continue;
            }
            headMap.put(index, getExcelPropertyValues(field, valueIndex));
            index++;
        }

        return headMap;
    }

    private static String getExcelPropertyValues(Field field, int valueIndex) {
        ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
        return excelProperty.value()[valueIndex];
    }

    public static HorizontalCellStyleStrategy getStyleStrategy(boolean isWriteImportTemplate) {
        // 头的策略
        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
        // 背景设置为黄色
        headWriteCellStyle.setFillForegroundColor(IndexedColors.YELLOW.getIndex());
        WriteFont headWriteFont = new WriteFont();
        headWriteFont.setFontHeightInPoints((short) 11);
        // 字体样式
        headWriteFont.setFontName("微软雅黑");
        headWriteCellStyle.setWriteFont(headWriteFont);
        // 自动换行
        headWriteCellStyle.setWrapped(false);
        // 水平对齐方式
        headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        // 垂直对齐方式
        headWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);

        // 内容的策略
        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
        contentWriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);
        // 背景白色
        contentWriteCellStyle.setFillForegroundColor(IndexedColors.LIGHT_TURQUOISE.getIndex());

        WriteFont contentWriteFont = new WriteFont();
        // 字体大小
        contentWriteFont.setFontHeightInPoints((short) 10);
        // 字体样式
        contentWriteFont.setFontName("微软雅黑");
        if (isWriteImportTemplate) {
            contentWriteFont.setColor(IndexedColors.GREY_40_PERCENT.getIndex());
        }
        
        contentWriteCellStyle.setWriteFont(contentWriteFont);
        // 设置底边框;
        contentWriteCellStyle.setBorderBottom(BorderStyle.THIN);
        // 设置底边框颜色;
        contentWriteCellStyle.setBottomBorderColor((short) 0);
        // 设置左边框;
        contentWriteCellStyle.setBorderLeft(BorderStyle.THIN);
        // 设置左边框颜色;
        contentWriteCellStyle.setLeftBorderColor((short) 0);
        // 设置右边框;
        contentWriteCellStyle.setBorderRight(BorderStyle.THIN);
        // 设置右边框颜色;
        contentWriteCellStyle.setRightBorderColor((short) 0);
        // 设置顶边框;
        contentWriteCellStyle.setBorderTop(BorderStyle.THIN);
        // 设置顶边框颜色;
        contentWriteCellStyle.setTopBorderColor((short) 0);
        // 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现
        return new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
    }

    public static <T> void writeAssetRoom(HttpServletResponse response, String fileName, String sheetName, List<T> data, Class<T> cls, WriteHandler writeHandler,
            List<Map<String, Object>> dataExample) throws IOException {
        writeAssetRoom(response, fileName, sheetName, data, cls, null, writeHandler, dataExample);
    }

    @SuppressWarnings("unchecked")
    public static <T> void writeAssetRoom(HttpServletResponse response, String fileName, String sheetName, List<T> data, Class<T> cls, Collection<String> excludeColumnFiledNames,
            WriteHandler writeHandler, List<Map<String, Object>> dataExample) throws IOException {
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
        response.setHeader("Content-Disposition", "attachment;filename=" + fileName);

        ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).build();
        WriteSheet writeSheet = new WriteSheet();
        ExcelWriterSheetBuilder sheetBuild = EasyExcel.writerSheet(0, sheetName).head(cls).registerWriteHandler(getStyleStrategy(false))
                .registerWriteHandler(new CustomCellWriteHandler());
        if (CollectionUtils.isNotEmpty(excludeColumnFiledNames)) {
            sheetBuild.excludeColumnFiledNames(excludeColumnFiledNames);
        }
        if (writeHandler != null) {
            sheetBuild.registerWriteHandler(writeHandler);
        }
        writeSheet = sheetBuild.build();
        excelWriter.write(data, writeSheet);

        for (Map<String, Object> map : dataExample) {
            Integer exampleSheetIndex = (Integer) map.get("sheetIndex");
            String exampleSheetName = (String) map.get("sheetName");
            List<T> exampleDataList = (List<T>) map.get("dataList");
            WriteSheet exampleWriteSheet = EasyExcel.writerSheet(exampleSheetIndex, exampleSheetName).head(cls).registerWriteHandler(getStyleStrategy(false))
                    .registerWriteHandler(new CustomCellWriteHandler()).build();
            excelWriter.write(exampleDataList, exampleWriteSheet);
        }
        excelWriter.finish();
    }

}
